package it.trekker.dao.ejb;

import java.util.List;
import it.trekker.dao.Excursion;
import it.trekker.dao.GPSCoordinate;
import it.trekker.dao.IExcursionDAO;
import it.trekker.dao.User;
import javax.annotation.security.RolesAllowed;
import javax.ejb.Local;
import javax.ejb.Stateless;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import org.jboss.security.annotation.SecurityDomain;


@SecurityDomain("TrekkerRealm")
@RolesAllowed("viewer")
@Stateless(mappedName="EJB3ExcursionDAO")
@Local(IExcursionDAO.class)
public class EJB3ExcursionDAO implements IExcursionDAO {
	
	@PersistenceContext(unitName = "TrekkerUnit")
	protected EntityManager em;

	public int insert(Excursion excursion) {
		if(excursion != null) {
			if(excursion.getUser() != null && excursion.getUser().getId() > 0)
				excursion.setUser(em.merge(excursion.getUser()));
			
			em.persist(excursion);
			return excursion.getId();
		}
		
		return 0;
	}

	public int remove(int id) {
		Excursion excursion = findExcursionById(id);
		
		if(excursion != null) {
			em.remove(excursion);
			return id;
		}
		
		return 0;
	}

	public int update(Excursion excursion) {
		if(excursion != null) {
			if(em.merge(excursion) != null)
				return excursion.getId();
		}
		
		return 0;
	}

	public Excursion findExcursionById(int id) {
		Excursion result = em.find(Excursion.class, id);
//		result = loadStartPoint(result);
		
		return result;
	}
	
	public Excursion findExcursionById(int id, boolean loadCoordinates) {
		Excursion result;
		
		if(loadCoordinates)
			result = (Excursion) em.createQuery("from Excursion e join fetch e.coordinates where e.id = :id").setParameter("id", id).getSingleResult();
		else
			result = em.find(Excursion.class, id);
		
//		result = loadStartPoint(result);
		
		return result;
	}
	
	public List<Excursion> findExcursionByName(String name) {
		List<Excursion> list = em.createQuery("from Excursion e join fetch e.coordinates where e.name = :name").setParameter("name", name).getResultList();
		
//		if(list != null) {
//			for(Excursion excursion : list) {
//				int index = list.indexOf(excursion);
//				list.remove(index);
//				list.add(index, loadStartPoint(excursion));
//			}
//		}
		
		return list;
	}

	public List<Excursion> findAllExcursions() {
		List<Excursion> list = em.createQuery("from Excursion").getResultList();
//		return loadStartPoints(list);
		return list;
	}

	public List<Excursion> findCloseExcursions(Excursion excursion, double radius) {
		return findCloseExcursions(excursion.getStartPoint(), radius, excursion);
	}
	
	
	public List<Excursion> findCloseExcursions(GPSCoordinate gpsCoord, double radius) {
		return findCloseExcursions(gpsCoord, radius, null);
	}
	
	/**
	 * Return excursions close to a given GPSCoordinate.
	 * @param gpsCoord: coordinate close to excursions to search.
	 * @param radius: radius of the searching area.
	 * @param excluded: excursion to exclude from result. It can be null.
	 * @return a list of excursions close to the given coordinate.
	 */
	private List<Excursion> findCloseExcursions(GPSCoordinate gpsCoord, double radius, Excursion excluded) {
		if(gpsCoord != null) {
				String sql = 	"SELECT DISTINCT(c.excursion) " +
								"FROM ExcursionCoordinate c JOIN FETCH c.excursion.coordinates " +
								"WHERE " +
									"( 6371 * " +
										"acos( " + 
											"cos( radians(:latitude) ) * cos( radians( c.coordinate.latitude ) ) * " + 
											"cos( radians( c.coordinate.longitude ) - radians(:longitude) ) + " + 
											"sin( radians(:latitude) ) * sin( radians( c.coordinate.latitude ) ) " + 
										") " +
									") < :radius ";
				if(excluded != null)
					sql +=  "AND c.excursion <> :excursion";
				
				Query query = em.createQuery(sql)
								.setParameter("latitude", gpsCoord.getLatitude())
								.setParameter("longitude", gpsCoord.getLongitude())
								.setParameter("radius", radius);
				
				if(excluded != null)
					query.setParameter("excursion", excluded);
				
				List<Excursion> list = query.setMaxResults(20).getResultList();
				
				return list;
		}
		
		return null;
	}

	public List<Excursion> findExcursionsByUser(User user) {
		if(user != null) {
			user = em.merge(user);
			
			return em.createQuery("from Excursion e where e.user =:user")
				.setParameter("user", user).getResultList();
		}
		
		return null;
	}
	

//	private Excursion loadStartPoint(Excursion excursion) {
//		if(excursion != null) {
////			GPSCoordinate startPoint = (GPSCoordinate) em.createQuery(	"SELECT DISTINCT (c) " +
////																		"FROM GPSCoordinate c " +
////																		"WHERE c.excursion = :excursion " +
////																		"ORDER BY c.timestamp")
////																		.setParameter("excursion", excursion)
////																		.setMaxResults(1)
////																		.getSingleResult();
////			
////			if(startPoint != null)
////				excursion.setStartPoint(startPoint);
//			
//			if(excursion.getCoordinates() != null && excursion.getCoordinates().size() > 0)
//				excursion.setStartPoint(excursion.getCoordinates().iterator().next());
//		}
//		
//		return excursion;
//	}
//
//	private List<Excursion> loadStartPoints(List<Excursion> list) {
//		List<Excursion> result = new ArrayList<Excursion>();
//		
//		if(list != null) {
//			for(Excursion excursion : list) {
//				int index = list.indexOf(excursion);
//				result.add(index, loadStartPoint(excursion));
//			}
//		}
//		
//		return list;
//	}
}
